#include "./BSP/LCD/lcd_init.h"
#include "./BSP/LCD/lcd.h"

/* --------------------------------------------- 全局变量 ------------------------------------------------ */

static uint8_t g_lcd_direction = 0;     /* LCD显示方向 */

#if USE_PWM_BLK
static uint8_t g_lcd_brightness = 100;  /* LCD显示亮度 */

TIM_HandleTypeDef g_lcd_blk_tim_handler;        /* BLK定时器句柄 */
TIM_OC_InitTypeDef g_lcd_blk_tim_oc_handler;    /* BLK定时器通道句柄 */
#endif

/* -------------------------------------------- 显示控制函数 ---------------------------------------------- */

/**
 * @brief 设置LCD显示方向
 * @param direction 显示方向(0-3)
 *        0: 0度
 *        1: 180度
 *        2: 90度
 *        3: 270度
 * @return 无
 */
void LCD_SetDirection(uint8_t direction)
{
    if (direction > 3) 
    {
        return; /* 无效的方向值 */
    }
    
    g_lcd_direction = direction;
    
    /* 重新配置LCD */
    LCD_WR_REG(0x36);  /* MADCTL: Memory Data Access Control */
    
    switch (direction)
    {
        case 0:  /* 0度 */
            LCD_WR_DATA8(0x00);
            break;
        case 1:  /* 180度 */
            LCD_WR_DATA8(0xC0);
            break;
        case 2:  /* 90度 */
            LCD_WR_DATA8(0x70);
            break;
        case 3:  /* 270度 */
            LCD_WR_DATA8(0xA0);
            break;
    }
    
    /* 重新设置显示区域 */
    switch (direction)
    {
        case 1:  /* 180度 */
        case 3:  /* 270度 */
            /* 横屏模式 */
            LCD_Address_Set(0, 0, LCD_H - 1, LCD_W - 1);
            break;
        default: /* 0度和90度 */
            /* 竖屏模式 */
            LCD_Address_Set(0, 0, LCD_W - 1, LCD_H - 1);
            break;
    }
    
    /* 清除屏幕内容 */
    LCD_Clear();
}

/**
 * @brief 获取当前LCD显示方向
 * @return 当前显示方向(0-3)
 */
uint8_t LCD_GetDirection(void)
{
    return g_lcd_direction;
}

/**
 * @brief 设置LCD背光亮度(0-100)
 * @param brightness 背光亮度(0-100)
 * @return  无
 */
void LCD_SetBacklight(uint8_t brightness)
{
    if (brightness > 100)
    {
        brightness = 100;
    }
#if USE_PWM_BLK
    /* 更新全局亮度变量 */
    g_lcd_brightness = brightness;

    /* 计算PWM占空比值 */
    uint32_t pulse = (brightness * (g_lcd_blk_tim_handler.Init.Period + 1)) / 100;

    /* 更新PWM占空比 */
    __HAL_TIM_SET_COMPARE(&g_lcd_blk_tim_handler, LCD_BLK_TIMX_CHY, pulse);
#endif
}

#if USE_HARDWARE_SPI
/* -------------------------------------------- 硬件SPI配置 -------------------------------------------- */
void SPI2_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* 使能SPI2和GPIO时钟 */
    __HAL_RCC_SPI2_CLK_ENABLE();                            /* 使能SPI2时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();                           /* 使能GPIOB时钟 */

    /* 配置SPI2引脚 */
    GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_15;          /* SCK: PB13, MOSI: PB15 */
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;                 /* 复用推挽输出 */
    GPIO_InitStruct.Pull = GPIO_NOPULL;                     /* 无上拉下拉 */
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;           /* 高速 */
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);                 /* 初始化GPIO */

    /* 配置SPI2参数 */
    hspi2.Instance = SPI2;                                  /* 选择SPI2 */
    hspi2.Init.Mode = SPI_MODE_MASTER;                      /* 主机模式 */
    hspi2.Init.Direction = SPI_DIRECTION_1LINE;             /* 单线发送模式 */
    hspi2.Init.DataSize = SPI_DATASIZE_8BIT;                /* 8位数据帧 */
    hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;             /* 时钟空闲时为高电平 */
    hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;                  /* 第二个时钟沿采样 */
    hspi2.Init.NSS = SPI_NSS_SOFT;                          /* 软件控制片选 */
    hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; /* 波特率分频因子2 */
    hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;                 /* 高位先行 */
    hspi2.Init.TIMode = SPI_TIMODE_DISABLE;                 /* 禁用TI模式 */
    hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* 禁用CRC */
    hspi2.Init.CRCPolynomial = 10;                          /* CRC多项式(未使用) */

    /* 初始化SPI2 */
    if (HAL_SPI_Init(&hspi2) != HAL_OK)                     /* 初始化配置 */
    {
        Error_Handler();
    }
}
#endif

/* -------------------------------------------- GPIO初始化 -------------------------------------------- */

/**
 * @brief LCD GPIO引脚初始化
 * @details 配置LCD所需的所有GPIO引脚，包括SCL、SDA、RES、DC、CS和BLK
 * @note 所有引脚配置为推挽输出，高速模式，无上拉下拉
 * @return 无
 */
void LCD_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* 使能时钟 */
#if !USE_HARDWARE_SPI
    LCD_SCL_GPIO_CLK_ENABLE();                              /* LCD_SCL脚时钟使能 */
    LCD_SDA_GPIO_CLK_ENABLE();                              /* LCD_SDA脚时钟使能 */
#endif
    LCD_RES_GPIO_CLK_ENABLE();                              /* LCD_RES脚时钟使能 */
    LCD_DC_GPIO_CLK_ENABLE();                               /* LCD_DC脚时钟使能 */
#if USE_LCD_CS
    LCD_CS_GPIO_CLK_ENABLE();                               /* LCD_CS脚时钟使能 */
#endif
    LCD_BLK_GPIO_CLK_ENABLE();                              /* LCD_BLK脚时钟使能 */

    /* 配置引脚 */
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;             /* 推挽复用 */
    GPIO_InitStruct.Pull = GPIO_NOPULL;                     /* 无上拉下拉 */
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;           /* 高速 */
    
#if !USE_HARDWARE_SPI
    GPIO_InitStruct.Pin = LCD_SCL_GPIO_PIN;
    HAL_GPIO_Init(LCD_SCL_GPIO_PORT, &GPIO_InitStruct);     /* LCD_SCL引脚模式设置(推挽输出) */

    GPIO_InitStruct.Pin = LCD_SDA_GPIO_PIN;
    HAL_GPIO_Init(LCD_SDA_GPIO_PORT, &GPIO_InitStruct);     /* 初始化LCD_SDA引脚 */
#endif

    GPIO_InitStruct.Pin = LCD_RES_GPIO_PIN;
    HAL_GPIO_Init(LCD_RES_GPIO_PORT, &GPIO_InitStruct);     /* 初始化LCD_RES引脚 */

    GPIO_InitStruct.Pin = LCD_DC_GPIO_PIN;
    HAL_GPIO_Init(LCD_DC_GPIO_PORT, &GPIO_InitStruct);      /* 初始化LCD_DC引脚 */

#if USE_LCD_CS
    GPIO_InitStruct.Pin = LCD_CS_GPIO_PIN;
    HAL_GPIO_Init(LCD_CS_GPIO_PORT, &GPIO_InitStruct);      /* 初始化LCD_CS引脚 */
#endif

#if USE_PWM_BLK
    /* 先使能时钟 */
    LCD_BLK_TIMX_CLK_ENABLE();                              /* 使能BLK引脚定时器时钟 */
    LCD_BLK_GPIO_CLK_ENABLE();                              /* 使能BLK引脚GPIO时钟 */
    
    /* 配置BLK引脚为PWM输出模式 */
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;                 /* 复用推挽输出 */
    GPIO_InitStruct.Pin = LCD_BLK_GPIO_PIN;                 /* 选择BLK引脚 */
    GPIO_InitStruct.Pull = GPIO_PULLUP;                     /* 上拉 */
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;           /* 高速 */
    HAL_GPIO_Init(LCD_BLK_GPIO_PORT, &GPIO_InitStruct);     /* 初始化BLK引脚为PWM输出 */
    LCD_BLK_TIMX_REMAP();                                    /* 执行定时器重映射 */
    
    /* 然后初始化PWM定时器 */
    LCD_BLK_PWM_Init(1250 - 1, 72 - 1);                     /* 初始化BLK PWM定时器，频率0.8kHz */
#else
    /* 配置BLK引脚为普通GPIO输出模式 */
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;             /* 推挽输出 */
    GPIO_InitStruct.Pin = LCD_BLK_GPIO_PIN;
    HAL_GPIO_Init(LCD_BLK_GPIO_PORT, &GPIO_InitStruct);     /* 初始化LCD_BLK引脚为普通输出 */
#endif

    /* 设置引脚初始电平 */
#if !USE_HARDWARE_SPI
    LCD_SCL(1);                                             /* SCL 默认高电平 */
    LCD_SDA(1);                                             /* SDA 默认高电平 */
#endif
    LCD_RES(1);                                             /* RES 默认高电平 */
    LCD_DC(1);                                              /* DC 默认高电平 */
#if USE_LCD_CS
    LCD_CS(1);                                              /* CS 默认高电平 */
#endif
#if !USE_PWM_BLK
    LCD_BLK(1);                                             /* BLK默认高电平 */
#endif
}

/* -------------------------------------------- SPI通信函数 -------------------------------------------- */

/**
 * @brief LCD串行数据写入函数
 * @param dat 要写入的串行数据，范围0x00-0xFF
 * @details 通过模拟SPI方式，将8位数据写入LCD
 * @note 数据按位从高到低依次写入，每位写入时序：SCL低-设置SDA-SCL高
 * @return 无
 */
void LCD_Writ_Bus(uint8_t dat)
{
#if USE_HARDWARE_SPI
    /* 使用硬件SPI发送数据 */
#if USE_LCD_CS
    LCD_CS(0);  /* 使能片选，开始传输 */
#endif
    HAL_SPI_Transmit(&hspi2, &dat, 1, HAL_MAX_DELAY);
#if USE_LCD_CS
    LCD_CS(1);  /* 禁用片选，结束传输 */
#endif
#else
    /* 使用软件模拟SPI发送数据 */
    uint8_t i;
#if USE_LCD_CS
    LCD_CS(0);  /* 使能片选，开始传输 */
#endif
    for (i = 0; i < 8; i++)
    {
        LCD_SCL(0);
        if (dat & 0x80)
        {
            LCD_SDA(1);
        }
        else
        {
            LCD_SDA(0);
        }
        LCD_SCL(1);
        dat <<= 1;
    }
#if USE_LCD_CS
    LCD_CS(1);  /* 禁用片选，结束传输 */
#endif
#endif
}

/**
 * @brief LCD写入8位数据
 * @param dat 要写入的8位数据，范围0x00-0xFF
 * @details 直接调用LCD_Writ_Bus函数写入8位数据
 * @return 无
 */
void LCD_WR_DATA8(uint8_t dat)
{
    LCD_Writ_Bus(dat);
}

/**
 * @brief LCD写入16位数据
 * @param dat 要写入的16位数据，范围0x0000-0xFFFF
 * @details 将16位数据分两次写入，先写高8位，再写低8位
 * @return 无
 */
void LCD_WR_DATA(uint16_t dat)
{
    LCD_Writ_Bus(dat >> 8);
    LCD_Writ_Bus(dat);
}

/**
 * @brief LCD写入命令
 * @param dat 要写入的命令，范围0x00-0xFF
 * @details 写入命令时DC引脚置低，写入后恢复高电平
 * @return 无
 */
void LCD_WR_REG(uint8_t dat)
{
    LCD_DC(0); /* 写命令 */
    LCD_Writ_Bus(dat);
    LCD_DC(1); /* 写数据 */
}

/**
 * @brief 设置LCD显示区域
 * @param x1 列起始地址，范围0~LCD_W-1
 * @param y1 行起始地址，范围0~LCD_H-1
 * @param x2 列结束地址，范围0~LCD_W-1
 * @param y2 行结束地址，范围0~LCD_H-1
 * @details 根据LCD的显示方向设置显示区域的坐标范围
 * @note 不同的显示方向(USE_HORIZONTAL)会影响坐标的映射关系
 * @return 无
 */
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
    uint16_t x_offset = 0;
    uint16_t y_offset = 0;
    
    switch (g_lcd_direction)
    {
        case 1:  /* 180度 */
            y_offset = 80;
            break;
        case 3:  /* 270度 */
            x_offset = 80;
            break;
        default: /* 0度和90度不需要偏移 */
            break;
    }
    
    LCD_WR_REG(0x2a);  /* 列地址设置 */
    LCD_WR_DATA(x1 + x_offset);
    LCD_WR_DATA(x2 + x_offset);
    LCD_WR_REG(0x2b);  /* 行地址设置 */
    LCD_WR_DATA(y1 + y_offset);
    LCD_WR_DATA(y2 + y_offset);
    LCD_WR_REG(0x2c);  /* 储存器写 */
}

/* -------------------------------------------- LCD初始化 -------------------------------------------- */

/**
 * @brief LCD初始化函数
 * @details 完成LCD的GPIO初始化、复位序列和寄存器配置
 * @note 初始化顺序：GPIO初始化->硬件复位->开启背光->寄存器配置
 * @return 无
 */
void LCD_Init(void)
{
    LCD_GPIO_Init(); /* 初始化GPIO引脚，配置SPI、复位、背光等控制信号 */

#if USE_HARDWARE_SPI
    SPI2_Init();  /* 初始化硬件SPI */
#endif

    LCD_RES(0);     /* 拉低复位引脚，启动硬件复位 */
    HAL_Delay(100); /* 维持复位信号至少5μs（手册7.4.5节要求，实际延时更久以确保稳定） */
    LCD_RES(1);     /* 释放复位引脚 */
    HAL_Delay(100); /* 等待复位完成（手册建议复位后至少等待5ms再发命令） */
    
#if USE_PWM_BLK
    /* 设置初始背光亮度 */
    LCD_SetBacklight(g_lcd_brightness);
    HAL_Delay(100); /* 背光稳定延时 */
#endif
    
    /* ------------------------------------------- 初始化序列开始 ----------------------------------------------- */
    /* 命令 0x11: Sleep Out (退出睡眠模式) */
    LCD_WR_REG(0x11); /* 9.1.12 SLPOUT命令，退出睡眠模式 */
    HAL_Delay(120);   /* 手册要求退出睡眠后至少等待120ms才能发送下一条命令 */

    /* 设置初始显示方向 */
    LCD_SetDirection(g_lcd_direction);

    /* 命令 0x3A: COLMOD (接口像素格式) */
    LCD_WR_REG(0x3A);   /* 9.1.32 COLMOD，设置颜色模式 */
    LCD_WR_DATA8(0x05); /* 参数0x05 = RGB565 (16位/像素)，参考8.7.3节 */

    /* 命令 0xB2: PORCTRL (前后廊设置) */
    LCD_WR_REG(0xB2);   /* 9.2.3 PORCTRL，设置垂直前后廊 */
    LCD_WR_DATA8(0x1F); /* 垂直前廊(VFP)设置为31行，增加帧间隔，减少残影 */
    LCD_WR_DATA8(0x1F); /* 垂直后廊(VBP)设置为31行，平衡前后廊，提高显示稳定性 */
    LCD_WR_DATA8(0x00); /* 保留位 */
    LCD_WR_DATA8(0x33); /* HSPD[7:0] = 0x33（水平同步宽度） */
    LCD_WR_DATA8(0x33); /* HBP/HFP分频设置（具体参考手册时序图） */

    /* 命令 0xB7: GCTRL (门控制) */
    LCD_WR_REG(0xB7);   /* 9.2.6 GCTRL，门驱动时序控制 */
    LCD_WR_DATA8(0x00); /* 禁用自动扫描模式(GAS=0)，使用标准门线数，降低功耗 */

    /* 命令 0xBB: VCOMS (VCOM电压设置) - 关键参数 */
    LCD_WR_REG(0xBB);   /* 9.2.9 VCOMS，设置VCOM电压 */
    LCD_WR_DATA8(0x3F); /* 设置较高的VCOM电压值，提高液晶分子复位效率，减少残影 */

    /* 命令 0xC0: LCMCTRL (LCM控制) */
    LCD_WR_REG(0xC0);   /* 9.2.10 LCMCTRL，液晶模块控制 */
    LCD_WR_DATA8(0x2C); /* 参数：XON=1, PON=1, VDV[4:0]=0x0C（驱动电压优化） */

    /* 命令 0xC2: VDVVRHEN (VDV/VRH使能) */
    LCD_WR_REG(0xC2);   /* 9.2.12 VDVVRHEN，使能VDV/VRH调节 */
    LCD_WR_DATA8(0x01); /* 参数：VEN=1（使能电压调节） */

    /* 命令 0xC3: VRHS (VRH电压设置) - 关键参数 */
    LCD_WR_REG(0xC3);   /* 9.2.13 VRHS，设置VGH电压 */
    LCD_WR_DATA8(0x0F); /* 设置VRH为4.3V，优化液晶驱动电压，提高响应速度，减少残影 */

    /* 命令 0xC4: VDVS (VDV电压设置) */
    LCD_WR_REG(0xC4);   /* 9.2.14 VDVS，设置VGL电压 */
    LCD_WR_DATA8(0x20); /* 参数：VDV[5:0]=0x20（典型值-4.0V） */

    /* 命令 0xC6: FRCTRL2 (正常模式帧率控制) - 关键参数 */
    LCD_WR_REG(0xC6);   /* 9.2.16 FRCTRL2，设置帧率 */
    LCD_WR_DATA8(0x13); /* 设置RTNA[5:0]=0x13，优化帧率约为70Hz，提高刷新速度，减少残影 */

    /* 命令 0xD0: PWCTRL1 (电源控制1) */
    LCD_WR_REG(0xD0);   /* 9.2.21 PWCTRL1，电源控制 */
    LCD_WR_DATA8(0xA4); /* AVDD=6.6V, AVCL=-4.8V */
    LCD_WR_DATA8(0xA1); /* VDS=1.6V（电压调节器设置） */

    /* 命令 0xD6: 睡眠模式设置 */
    LCD_WR_REG(0xD6);   /* 睡眠模式设置 */
    LCD_WR_DATA8(0xA1); /* 设置睡眠模式下gate输出为GND，降低睡眠模式下功耗 */

    /* ------------------------------------------------ 伽马校正设置 ------------------------------------------------ */
    /* 命令 0xE0: PVGAMCTRL (正电压伽马校正) - 关键参数 */
    LCD_WR_REG(0xE0);   /* 9.2.24 PVGAMCTRL，正伽马校正（14个参数） */
    LCD_WR_DATA8(0xF0); /* VP0：高灰阶电压调整，优化高亮度区域显示 */
    LCD_WR_DATA8(0x06); /* VP1：低灰阶电压微调，改善暗部细节 */
    LCD_WR_DATA8(0x0D); /* VP2：灰阶过渡区域1电压调整 */
    LCD_WR_DATA8(0x0B); /* VP3：灰阶过渡区域2电压调整 */
    LCD_WR_DATA8(0x0A); /* VP4：灰阶过渡区域3电压调整 */
    LCD_WR_DATA8(0x07); /* VP5：中灰阶电压调整，优化色彩过渡 */
    LCD_WR_DATA8(0x2E); /* VP6：中高灰阶电压调整 */
    LCD_WR_DATA8(0x43); /* VP7：高灰阶区域1电压调整 */
    LCD_WR_DATA8(0x45); /* VP8：高灰阶区域2电压调整，增强对比度 */
    LCD_WR_DATA8(0x38); /* VP9：高灰阶区域3电压调整 */
    LCD_WR_DATA8(0x14); /* VP10：过渡区域电压微调 */
    LCD_WR_DATA8(0x13); /* VP11：低灰阶区域电压微调 */
    LCD_WR_DATA8(0x25); /* VP12：最低灰阶区域电压调整 */
    LCD_WR_DATA8(0x29); /* VP13：黑电平电压调整，优化黑色显示效果 */

    /* 命令 0xE1: NVGAMCTRL (负电压伽马校正) - 关键参数 */
    LCD_WR_REG(0xE1);   /* 9.2.25 NVGAMCTRL，负伽马校正（14个参数） */
    LCD_WR_DATA8(0xF0); /* VN0：负高灰阶电压调整，平衡正负伽马曲线 */
    LCD_WR_DATA8(0x07); /* VN1：负低灰阶电压微调 */
    LCD_WR_DATA8(0x0A); /* VN2：负灰阶过渡区域1电压调整 */
    LCD_WR_DATA8(0x08); /* VN3：负灰阶过渡区域2电压调整 */
    LCD_WR_DATA8(0x07); /* VN4：负灰阶过渡区域3电压调整 */
    LCD_WR_DATA8(0x23); /* VN5：负中灰阶电压调整，优化色彩过渡 */
    LCD_WR_DATA8(0x2E); /* VN6：负中高灰阶电压调整 */
    LCD_WR_DATA8(0x33); /* VN7：负高灰阶区域1电压调整 */
    LCD_WR_DATA8(0x44); /* VN8：负高灰阶区域2电压调整，增强对比度 */
    LCD_WR_DATA8(0x3A); /* VN9：负高灰阶区域3电压调整 */
    LCD_WR_DATA8(0x16); /* VN10：负过渡区域电压微调 */
    LCD_WR_DATA8(0x17); /* VN11：负低灰阶区域电压微调 */
    LCD_WR_DATA8(0x26); /* VN12：负最低灰阶区域电压调整 */
    LCD_WR_DATA8(0x2C); /* VN13：负黑电平电压调整，优化黑色显示效果 */

    /* 命令 0xE4: Gate控制 */
    LCD_WR_REG(0xE4);   /* Gate控制参数设置 */
    LCD_WR_DATA8(0x1D); /* 使用240根gate线 ((N+1)*8=240)，匹配LCD物理分辨率 */
    LCD_WR_DATA8(0x00); /* 设定gate起点位置为0，从顶部开始扫描 */
    LCD_WR_DATA8(0x00); /* 当gate没有用完时，TMG位设为0，使用标准时序 */

    /* 命令 0x21: INVON (显示反转开启) */
    LCD_WR_REG(0x21); /* 9.1.16 INVON，开启像素反转（改善显示效果） */

    /* 在显示开启前清屏，解决花屏问题 */
    LCD_Fill(0, 0, LCD_W, LCD_H, g_back_color);
    
    /* 命令 0x29: DISPON (显示开启) */
    LCD_WR_REG(0x29); /* 9.1.19 DISPON，最后使能显示输出 */
}

#if USE_PWM_BLK
/**
 * @brief      BLK PWM定时器初始化函数
 * @param[in]  arr         自动重装载值,决定PWM频率
 * @param[in]  psc         时钟预分频数,决定定时器计数频率
 *
 * @note       定时器的时钟来自APB1 / APB2, 当APB1 / APB2 分频时, 定时器频率自动翻倍
 *             定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *             Ft = 定时器工作频率, 单位: Mhz
 * @retval     无
 */
void LCD_BLK_PWM_Init(uint16_t arr, uint16_t psc)
{
    /* 配置定时器基本参数 */
    g_lcd_blk_tim_handler.Instance = LCD_BLK_TIMX;                  /* BLK定时器 */
    g_lcd_blk_tim_handler.Init.Prescaler = psc;                     /* 设置预分频器 */
    g_lcd_blk_tim_handler.Init.CounterMode = TIM_COUNTERMODE_UP;    /* 向上计数模式 */
    g_lcd_blk_tim_handler.Init.Period = arr;                        /* 自动重装载值 */
    HAL_TIM_PWM_Init(&g_lcd_blk_tim_handler);                       /* 初始化PWM定时器 */

    /* 配置PWM通道参数 */
    g_lcd_blk_tim_oc_handler.OCMode = TIM_OCMODE_PWM1;              /* PWM模式1: CNT<CCR时输出有效电平 */
    g_lcd_blk_tim_oc_handler.Pulse = arr;                           /* 设置比较值,此值用来确定占空比，这里默认设置比较值为自动重装载值,即占空比为100% */
    g_lcd_blk_tim_oc_handler.OCPolarity = TIM_OCPOLARITY_HIGH;      /* 输出极性为高电平有效 */
    HAL_TIM_PWM_ConfigChannel(&g_lcd_blk_tim_handler, &g_lcd_blk_tim_oc_handler, LCD_BLK_TIMX_CHY); /* 配置BLK定时器通道 */

    /* 启动PWM输出 */
    HAL_TIM_PWM_Start(&g_lcd_blk_tim_handler, LCD_BLK_TIMX_CHY);
}

/**
 * @brief      定时器PWM底层初始化回调函数
 * @param[in]  htim        定时器句柄指针
 *
 * @note       该函数会被HAL_TIM_PWM_Init()自动调用
 * @retval     无
 */
//void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
//{
//   GPIO_InitTypeDef gpio_init_struct;

//   if (htim->Instance == LCD_BLK_TIMX)
//   {
//       LCD_BLK_TIMX_CLK_ENABLE();                            /* 使能BLK引脚定时器时钟 */
//       LCD_BLK_GPIO_CLK_ENABLE();                            /* 使能BLK引脚GPIO时钟 */

//       gpio_init_struct.Pin = LCD_BLK_GPIO_PIN;              /* 选择BLK引脚 */
//       gpio_init_struct.Mode = GPIO_MODE_AF_PP;              /* 复用推挽输出 */
//       gpio_init_struct.Pull = GPIO_PULLUP;                  /* 上拉 */
//       gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;        /* 高速 */
//       HAL_GPIO_Init(LCD_BLK_GPIO_PORT, &gpio_init_struct);  /* 初始化BLK引脚 */
//       LCD_BLK_TIMX_REMAP();                                 /* 执行定时器重映射 */
//   }
//}
#endif
